package clock.socoolby.com.clock.widget.animatorview.animator;


import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Region;

import java.util.List;
import java.util.Random;

import clock.socoolby.com.clock.widget.animatorview.AbstractAnimator;
import clock.socoolby.com.clock.widget.animatorview.I_AnimatorEntry;
import quatja.com.vorolay.diagram.Voronoi;
import quatja.com.vorolay.diagram.VoronoiRegion;

//参考自:https://github.com/Quatja/Vorolay
public class VorolayAnimator extends AbstractAnimator<VorolayAnimator.Vorolay> {
    public static final String NAME="Vorolay";


    /**
     * Diagram sites are generating randomly inside the view bounds.
     * Use with and {@code app:generation_type}
     */
    final static public int GENERATION_TYPE_RANDOM = 1;
    /**
     * Diagram sites are generating like a grid-view with random offsets
     * Use with {setGenerationType} and {@code app:generation_type}
     */
    final static public int GENERATION_TYPE_ORDERED = 2;
    /**
     * Diagram sites are user-defined.
     * Use with {setGenerationType} and {@code app:generation_type}
     */
    final static public int GENERATION_TYPE_CUSTOM = 3;

    /*
     *   DEFAULTS
     */
    final static private boolean DEF_BORDERS = true;
    final static private int DEF_BORDERS_COLOR = Color.LTGRAY;
    final static private float DEF_BORDER_WIDTH = 3.5f;
    final static private boolean DEF_BORDER_CORNERS_ROUND = true;
    final static private int DEF_GENERATION_TYPE = GENERATION_TYPE_RANDOM;

    int regionsCount;

    public VorolayAnimator( ) {
       this(15);
    }

    public VorolayAnimator(int regionsCount,int delay) {
        super(1,delay);
        this.regionsCount=regionsCount;
    }

    public VorolayAnimator(int regionsCount) {
       this(regionsCount,600);
    }

    @Override
    public int delayTime() {
        return delayRunLoopTime;
    }

    @Override
    public Vorolay createNewEntry() {
        return new Vorolay(width,height,regionsCount);
    }


    public class Vorolay implements I_AnimatorEntry {
        /*
         *   FIELDS
         */

        private Paint p;

        private Voronoi mVoronoi;
        private List<VoronoiRegion> mRegions;
        private List<VoronoiRegion.VoronoiPoint> mCustomPoints;

        private int mRegionsCount;
        private int mViewWidth, mViewHeight;
        private int mDistanceBetweenSites = 20;

        /*
         *   ATTRIBUTES
         */
        private boolean mBorderEnabled;
        private int mBorderColor;
        private float mBorderWidth;
        private boolean mRoundCornersEnabled;
        private int mGenerationType;
        private int alpha=50;

        public Vorolay(int mViewWidth, int mViewHeight,int mRegionsCount) {
            this.mViewWidth = mViewWidth;
            this.mViewHeight = mViewHeight;
            this.mRegionsCount=mRegionsCount;
            init();
        }

        private void init() {
            mBorderEnabled = DEF_BORDERS;
            mBorderColor =  DEF_BORDERS_COLOR;
            mBorderWidth =  DEF_BORDER_WIDTH;
            mRoundCornersEnabled =  DEF_BORDER_CORNERS_ROUND;
            mGenerationType = DEF_GENERATION_TYPE;
            initPaint();
            initDiagram();
            generateDiagram();
        }

        private void initDiagram() {
            mVoronoi = new Voronoi(mDistanceBetweenSites);
        }

        private void initPaint() {
            p = new Paint();
            p.setAntiAlias(true);
            p.setColor(mBorderColor);
            p.setStrokeWidth(mBorderWidth);
            if(randColor)
              p.setStyle(Paint.Style.FILL_AND_STROKE);
            else
                p.setStyle(Paint.Style.STROKE);
            p.setAlpha(alpha);
            if (mRoundCornersEnabled) {
                p.setStrokeJoin(Paint.Join.ROUND);
                p.setStrokeCap(Paint.Cap.SQUARE);
                p.setPathEffect(new CornerPathEffect(15));
            }
        }


        private void generateDiagram() {
            if (mViewWidth == 0 || mViewHeight == 0) {
                return;
            }

            double[] arrayX = new double[mRegionsCount];
            double[] arrayY = new double[mRegionsCount];

            switch (mGenerationType) {
                case GENERATION_TYPE_RANDOM:
                    generateRandomPoints(arrayX, arrayY);
                    break;
                case GENERATION_TYPE_ORDERED:
                    generateOrderedPoints(arrayX, arrayY);
                    break;
                case GENERATION_TYPE_CUSTOM:
                    generateCustomPoints(arrayX, arrayY);
                    break;
                default:
                    generateRandomPoints(arrayX, arrayY);
                    break;
            }


            mVoronoi.generateVoronoi(arrayX, arrayY, 0, mViewWidth, 0, mViewHeight);
            mRegions = mVoronoi.getRegions();
        }

        /*
         *
         * Points generation methods
         *
         */

        private void generateRandomPoints(double[] arrayX, double[] arrayY) {
            Random rand = new Random();

            int i = 0;
            while (i < mRegionsCount) {
                int x1 = rand.nextInt(mViewWidth + 1);
                int y1 = rand.nextInt(mViewHeight + 1);

                boolean good = true;
                for (int j = 0; j < arrayX.length; j++) {
                    double x2 = arrayX[j];
                    double y2 = arrayY[j];

                    double dist = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
                    if (dist < mDistanceBetweenSites)
                        good = false;
                }

                if (good) {
                    arrayX[i] = x1;
                    arrayY[i] = y1;
                    i++;
                }
            }
        }


        private void generateOrderedPoints(double[] arrayX, double[] arrayY) {
            Random rand = new Random();

            // Make a number of columns
            int diff_w = Math.max(mViewWidth / mViewHeight, 1);
            int diff_h = Math.max(mViewHeight / mViewWidth, 1);
            boolean correct = false;
            int temp_col = 0;
            int temp_row = 0;
            while (!correct) {
                temp_col += diff_w;
                temp_row += diff_h;
                if (temp_col * temp_row >= mRegionsCount)
                    correct = true;
            }


            // Make a number of rows
            int columns = temp_col;
            int bal = mRegionsCount % columns;
            int rows = mRegionsCount / columns;

            // Region width/height
            int reg_width = mViewWidth / columns;
            int reg_height = mViewHeight / rows;


            // Generate points
            int curX = 0;
            int curY = 0;
            int index = 0;
            for (int i = 0; i < rows; i++) {
                if (i == rows - 1 && bal > 0) {
                    columns += bal;
                    reg_width = mViewWidth / columns;
                }
                for (int j = 0; j < columns; j++) {
                    int x1 = rand.nextInt(reg_width / 2) + curX + reg_width / 4;
                    int y1 = rand.nextInt(reg_height / 2) + curY + reg_height / 4;

                    arrayX[index] = x1;
                    arrayY[index] = y1;
                    index++;

                    curX += reg_width;
                }
                curX = 0;
                curY += reg_height;
            }
        }

        private void generateCustomPoints(double[] arrayX, double[] arrayY) {

            if (mCustomPoints == null || mCustomPoints.size() != mRegionsCount) {
                throw new RuntimeException("The custom points count is not equals to a regions count");
            }

            for (int i = 0; i < mCustomPoints.size(); i++) {
                VoronoiRegion.VoronoiPoint point = mCustomPoints.get(i);
                arrayX[i] = point.x;
                arrayY[i] = point.y;
            }
        }

        @Override
        public void move(int maxWidth, int maxHight) {

        }

        @Override
        public void onDraw(Canvas canvas, Paint mPaint) {
            for (VoronoiRegion region : mRegions) {
                // firstly clip and draw children
                canvas.clipPath(region.path, Region.Op.REPLACE);

                // then draw borders
                if (mBorderEnabled) {
                    randomColorIfAble();
                    p.setColor(color);
                    canvas.drawPath(region.path, p);
                }

                // draw site
                // canvas.drawBorder((float)region.site.percent, (float)region.site.y, 10, p);
            }
        }

        @Override
        public void setAnimatorEntryColor(int color) {
            p.setColor(color);
        }
    }
}
